home *** CD-ROM | disk | FTP | other *** search
/ SGI Freeware 1999 August / SGI Freeware 1999 August.iso / dist / fw_perl.idb / usr / freeware / lib / perl5 / 5.00502 / Pod / Html.pm.z / Html.pm
Encoding:
Perl POD Document  |  1998-10-28  |  41.6 KB  |  1,572 lines

  1. package Pod::Html;
  2.  
  3. use Pod::Functions;
  4. use Getopt::Long;    # package for handling command-line parameters
  5. require Exporter;
  6. use vars qw($VERSION);
  7. $VERSION = 1.01;
  8. @ISA = Exporter;
  9. @EXPORT = qw(pod2html htmlify);
  10. use Cwd;
  11.  
  12. use Carp;
  13.  
  14. use strict;
  15.  
  16. use Config;
  17.  
  18. =head1 NAME
  19.  
  20. Pod::Html - module to convert pod files to HTML
  21.  
  22. =head1 SYNOPSIS
  23.  
  24.     use Pod::Html;
  25.     pod2html([options]);
  26.  
  27. =head1 DESCRIPTION
  28.  
  29. Converts files from pod format (see L<perlpod>) to HTML format.  It
  30. can automatically generate indexes and cross-references, and it keeps
  31. a cache of things it knows how to cross-reference.
  32.  
  33. =head1 ARGUMENTS
  34.  
  35. Pod::Html takes the following arguments:
  36.  
  37. =over 4
  38.  
  39. =item help
  40.  
  41.     --help
  42.  
  43. Displays the usage message.
  44.  
  45. =item htmlroot
  46.  
  47.     --htmlroot=name
  48.  
  49. Sets the base URL for the HTML files.  When cross-references are made,
  50. the HTML root is prepended to the URL.
  51.  
  52. =item infile
  53.  
  54.     --infile=name
  55.  
  56. Specify the pod file to convert.  Input is taken from STDIN if no
  57. infile is specified.
  58.  
  59. =item outfile
  60.  
  61.     --outfile=name
  62.  
  63. Specify the HTML file to create.  Output goes to STDOUT if no outfile
  64. is specified.
  65.  
  66. =item podroot
  67.  
  68.     --podroot=name
  69.  
  70. Specify the base directory for finding library pods.
  71.  
  72. =item podpath
  73.  
  74.     --podpath=name:...:name
  75.  
  76. Specify which subdirectories of the podroot contain pod files whose
  77. HTML converted forms can be linked-to in cross-references.
  78.  
  79. =item libpods
  80.  
  81.     --libpods=name:...:name
  82.  
  83. List of page names (eg, "perlfunc") which contain linkable C<=item>s.
  84.  
  85. =item netscape
  86.  
  87.     --netscape
  88.  
  89. Use Netscape HTML directives when applicable.
  90.  
  91. =item nonetscape
  92.  
  93.     --nonetscape
  94.  
  95. Do not use Netscape HTML directives (default).
  96.  
  97. =item index
  98.  
  99.     --index
  100.  
  101. Generate an index at the top of the HTML file (default behaviour).
  102.  
  103. =item noindex
  104.  
  105.     --noindex
  106.  
  107. Do not generate an index at the top of the HTML file.
  108.  
  109.  
  110. =item recurse
  111.  
  112.     --recurse
  113.  
  114. Recurse into subdirectories specified in podpath (default behaviour).
  115.  
  116. =item norecurse
  117.  
  118.     --norecurse
  119.  
  120. Do not recurse into subdirectories specified in podpath.
  121.  
  122. =item title
  123.  
  124.     --title=title
  125.  
  126. Specify the title of the resulting HTML file.
  127.  
  128. =item verbose
  129.  
  130.     --verbose
  131.  
  132. Display progress messages.
  133.  
  134. =back
  135.  
  136. =head1 EXAMPLE
  137.  
  138.     pod2html("pod2html",
  139.          "--podpath=lib:ext:pod:vms", 
  140.          "--podroot=/usr/src/perl",
  141.          "--htmlroot=/perl/nmanual",
  142.          "--libpods=perlfunc:perlguts:perlvar:perlrun:perlop",
  143.          "--recurse",
  144.          "--infile=foo.pod",
  145.          "--outfile=/perl/nmanual/foo.html");
  146.  
  147. =head1 AUTHOR
  148.  
  149. Tom Christiansen, E<lt>tchrist@perl.comE<gt>.
  150.  
  151. =head1 BUGS
  152.  
  153. Has trouble with C<> etc in = commands.
  154.  
  155. =head1 SEE ALSO
  156.  
  157. L<perlpod>
  158.  
  159. =head1 COPYRIGHT
  160.  
  161. This program is distributed under the Artistic License.
  162.  
  163. =cut
  164.  
  165. my $dircache = "pod2html-dircache";
  166. my $itemcache = "pod2html-itemcache";
  167.  
  168. my @begin_stack = ();        # begin/end stack
  169.  
  170. my @libpods = ();            # files to search for links from C<> directives
  171. my $htmlroot = "/";            # http-server base directory from which all
  172.                 #   relative paths in $podpath stem.
  173. my $htmlfile = "";        # write to stdout by default
  174. my $podfile = "";        # read from stdin by default
  175. my @podpath = ();        # list of directories containing library pods.
  176. my $podroot = ".";        # filesystem base directory from which all
  177.                 #   relative paths in $podpath stem.
  178. my $recurse = 1;        # recurse on subdirectories in $podpath.
  179. my $verbose = 0;        # not verbose by default
  180. my $doindex = 1;               # non-zero if we should generate an index
  181. my $listlevel = 0;        # current list depth
  182. my @listitem = ();        # stack of HTML commands to use when a =item is
  183.                 #   encountered.  the top of the stack is the
  184.                 #   current list.
  185. my @listdata = ();        # similar to @listitem, but for the text after
  186.                 #   an =item
  187. my @listend = ();        # similar to @listitem, but the text to use to
  188.                 #   end the list.
  189. my $ignore = 1;            # whether or not to format text.  we don't
  190.                 #   format text until we hit our first pod
  191.                 #   directive.
  192.  
  193. my %items_named = ();        # for the multiples of the same item in perlfunc
  194. my @items_seen = ();
  195. my $netscape = 0;        # whether or not to use netscape directives.
  196. my $title;            # title to give the pod(s)
  197. my $top = 1;            # true if we are at the top of the doc.  used
  198.                 #   to prevent the first <HR> directive.
  199. my $paragraph;            # which paragraph we're processing (used
  200.                 #   for error messages)
  201. my %pages = ();            # associative array used to find the location
  202.                 #   of pages referenced by L<> links.
  203. my %sections = ();        # sections within this page
  204. my %items = ();            # associative array used to find the location
  205.                 #   of =item directives referenced by C<> links
  206. my $Is83;                       # is dos with short filenames (8.3)
  207.  
  208. sub init_globals {
  209. $dircache = "pod2html-dircache";
  210. $itemcache = "pod2html-itemcache";
  211.  
  212. @begin_stack = ();        # begin/end stack
  213.  
  214. @libpods = ();            # files to search for links from C<> directives
  215. $htmlroot = "/";            # http-server base directory from which all
  216.                 #   relative paths in $podpath stem.
  217. $htmlfile = "";        # write to stdout by default
  218. $podfile = "";        # read from stdin by default
  219. @podpath = ();        # list of directories containing library pods.
  220. $podroot = ".";        # filesystem base directory from which all
  221.                 #   relative paths in $podpath stem.
  222. $recurse = 1;        # recurse on subdirectories in $podpath.
  223. $verbose = 0;        # not verbose by default
  224. $doindex = 1;               # non-zero if we should generate an index
  225. $listlevel = 0;        # current list depth
  226. @listitem = ();        # stack of HTML commands to use when a =item is
  227.                 #   encountered.  the top of the stack is the
  228.                 #   current list.
  229. @listdata = ();        # similar to @listitem, but for the text after
  230.                 #   an =item
  231. @listend = ();        # similar to @listitem, but the text to use to
  232.                 #   end the list.
  233. $ignore = 1;            # whether or not to format text.  we don't
  234.                 #   format text until we hit our first pod
  235.                 #   directive.
  236.  
  237. @items_seen = ();
  238. %items_named = ();
  239. $netscape = 0;        # whether or not to use netscape directives.
  240. $title = '';            # title to give the pod(s)
  241. $top = 1;            # true if we are at the top of the doc.  used
  242.                 #   to prevent the first <HR> directive.
  243. $paragraph = '';            # which paragraph we're processing (used
  244.                 #   for error messages)
  245. %sections = ();        # sections within this page
  246.  
  247. # These are not reinitialised here but are kept as a cache.
  248. # See get_cache and related cache management code.
  249. #%pages = ();            # associative array used to find the location
  250.                 #   of pages referenced by L<> links.
  251. #%items = ();            # associative array used to find the location
  252.                 #   of =item directives referenced by C<> links
  253. $Is83=$^O eq 'dos';
  254. }
  255.  
  256. sub pod2html {
  257.     local(@ARGV) = @_;
  258.     local($/);
  259.     local $_;
  260.  
  261.     init_globals();
  262.  
  263.     $Is83 = 0 if (defined (&Dos::UseLFN) && Dos::UseLFN());
  264.  
  265.     # cache of %pages and %items from last time we ran pod2html
  266.  
  267.     #undef $opt_help if defined $opt_help;
  268.  
  269.     # parse the command-line parameters
  270.     parse_command_line();
  271.  
  272.     # set some variables to their default values if necessary
  273.     local *POD;
  274.     unless (@ARGV && $ARGV[0]) { 
  275.     $podfile  = "-" unless $podfile;    # stdin
  276.     open(POD, "<$podfile")
  277.         || die "$0: cannot open $podfile file for input: $!\n";
  278.     } else {
  279.     $podfile = $ARGV[0];  # XXX: might be more filenames
  280.     *POD = *ARGV;
  281.     } 
  282.     $htmlfile = "-" unless $htmlfile;    # stdout
  283.     $htmlroot = "" if $htmlroot eq "/";    # so we don't get a //
  284.  
  285.     # read the pod a paragraph at a time
  286.     warn "Scanning for sections in input file(s)\n" if $verbose;
  287.     $/ = "";
  288.     my @poddata  = <POD>;
  289.     close(POD);
  290.  
  291.     # scan the pod for =head[1-6] directives and build an index
  292.     my $index = scan_headings(\%sections, @poddata);
  293.  
  294.     unless($index) {
  295.     warn "No pod in $podfile\n" if $verbose;
  296.     return;
  297.     }
  298.  
  299.     # open the output file
  300.     open(HTML, ">$htmlfile")
  301.         || die "$0: cannot open $htmlfile file for output: $!\n";
  302.  
  303.     # put a title in the HTML file
  304.     $title = '';
  305.     TITLE_SEARCH: {
  306.     for (my $i = 0; $i < @poddata; $i++) { 
  307.         if ($poddata[$i] =~ /^=head1\s*NAME\b/m) {
  308.         for my $para ( @poddata[$i, $i+1] ) { 
  309.             last TITLE_SEARCH if ($title) = $para =~ /(\S+\s+-+.*\S)/s;
  310.         }
  311.         } 
  312.  
  313.     } 
  314.     } 
  315.     if (!$title and $podfile =~ /\.pod$/) {
  316.     # probably a split pod so take first =head[12] as title
  317.     for (my $i = 0; $i < @poddata; $i++) { 
  318.         last if ($title) = $poddata[$i] =~ /^=head[12]\s*(.*)/;
  319.     } 
  320.     warn "adopted '$title' as title for $podfile\n"
  321.         if $verbose and $title;
  322.     } 
  323.     if ($title) {
  324.     $title =~ s/\s*\(.*\)//;
  325.     } else {
  326.     warn "$0: no title for $podfile";
  327.     $podfile =~ /^(.*)(\.[^.\/]+)?$/;
  328.     $title = ($podfile eq "-" ? 'No Title' : $1);
  329.     warn "using $title" if $verbose;
  330.     }
  331.     print HTML <<END_OF_HEAD;
  332. <HTML>
  333. <HEAD>
  334. <TITLE>$title</TITLE>
  335. <LINK REV="made" HREF="mailto:$Config{perladmin}">
  336. </HEAD>
  337.  
  338. <BODY>
  339.  
  340. END_OF_HEAD
  341.  
  342.     # load/reload/validate/cache %pages and %items
  343.     get_cache($dircache, $itemcache, \@podpath, $podroot, $recurse);
  344.  
  345.     # scan the pod for =item directives
  346.     scan_items("", \%items, @poddata);
  347.  
  348.     # put an index at the top of the file.  note, if $doindex is 0 we
  349.     # still generate an index, but surround it with an html comment.
  350.     # that way some other program can extract it if desired.
  351.     $index =~ s/--+/-/g;
  352.     print HTML "<!-- INDEX BEGIN -->\n";
  353.     print HTML "<!--\n" unless $doindex;
  354.     print HTML $index;
  355.     print HTML "-->\n" unless $doindex;
  356.     print HTML "<!-- INDEX END -->\n\n";
  357.     print HTML "<HR>\n" if $doindex;
  358.  
  359.     # now convert this file
  360.     warn "Converting input file\n" if $verbose;
  361.     foreach my $i (0..$#poddata) {
  362.     $_ = $poddata[$i];
  363.     $paragraph = $i+1;
  364.     if (/^(=.*)/s) {    # is it a pod directive?
  365.         $ignore = 0;
  366.         $_ = $1;
  367.         if (/^=begin\s+(\S+)\s*(.*)/si) {# =begin
  368.         process_begin($1, $2);
  369.         } elsif (/^=end\s+(\S+)\s*(.*)/si) {# =end
  370.         process_end($1, $2);
  371.         } elsif (/^=cut/) {            # =cut
  372.         process_cut();
  373.         } elsif (/^=pod/) {            # =pod
  374.         process_pod();
  375.         } else {
  376.         next if @begin_stack && $begin_stack[-1] ne 'html';
  377.  
  378.         if (/^=(head[1-6])\s+(.*\S)/s) {    # =head[1-6] heading
  379.             process_head($1, $2);
  380.         } elsif (/^=item\s*(.*\S)/sm) {    # =item text
  381.             process_item($1);
  382.         } elsif (/^=over\s*(.*)/) {        # =over N
  383.             process_over();
  384.         } elsif (/^=back/) {        # =back
  385.             process_back();
  386.         } elsif (/^=for\s+(\S+)\s+(.*)/si) {# =for
  387.             process_for($1,$2);
  388.         } else {
  389.             /^=(\S*)\s*/;
  390.             warn "$0: $podfile: unknown pod directive '$1' in "
  391.                . "paragraph $paragraph.  ignoring.\n";
  392.         }
  393.         }
  394.         $top = 0;
  395.     }
  396.     else {
  397.         next if $ignore;
  398.         next if @begin_stack && $begin_stack[-1] ne 'html';
  399.         my $text = $_;
  400.         process_text(\$text, 1);
  401.         print HTML "<P>\n$text";
  402.     }
  403.     }
  404.  
  405.     # finish off any pending directives
  406.     finish_list();
  407.     print HTML <<END_OF_TAIL;
  408. </BODY>
  409.  
  410. </HTML>
  411. END_OF_TAIL
  412.  
  413.     # close the html file
  414.     close(HTML);
  415.  
  416.     warn "Finished\n" if $verbose;
  417. }
  418.  
  419. ##############################################################################
  420.  
  421. my $usage;            # see below
  422. sub usage {
  423.     my $podfile = shift;
  424.     warn "$0: $podfile: @_\n" if @_;
  425.     die $usage;
  426. }
  427.  
  428. $usage =<<END_OF_USAGE;
  429. Usage:  $0 --help --htmlroot=<name> --infile=<name> --outfile=<name>
  430.            --podpath=<name>:...:<name> --podroot=<name>
  431.            --libpods=<name>:...:<name> --recurse --verbose --index
  432.            --netscape --norecurse --noindex
  433.  
  434.   --flush      - flushes the item and directory caches.
  435.   --help       - prints this message.
  436.   --htmlroot   - http-server base directory from which all relative paths
  437.                  in podpath stem (default is /).
  438.   --index      - generate an index at the top of the resulting html
  439.                  (default).
  440.   --infile     - filename for the pod to convert (input taken from stdin
  441.                  by default).
  442.   --libpods    - colon-separated list of pages to search for =item pod
  443.                  directives in as targets of C<> and implicit links (empty
  444.                  by default).  note, these are not filenames, but rather
  445.                  page names like those that appear in L<> links.
  446.   --netscape   - will use netscape html directives when applicable.
  447.   --nonetscape - will not use netscape directives (default).
  448.   --outfile    - filename for the resulting html file (output sent to
  449.                  stdout by default).
  450.   --podpath    - colon-separated list of directories containing library
  451.                  pods.  empty by default.
  452.   --podroot    - filesystem base directory from which all relative paths
  453.                  in podpath stem (default is .).
  454.   --noindex    - don't generate an index at the top of the resulting html.
  455.   --norecurse  - don't recurse on those subdirectories listed in podpath.
  456.   --recurse    - recurse on those subdirectories listed in podpath
  457.                  (default behavior).
  458.   --title      - title that will appear in resulting html file.
  459.   --verbose    - self-explanatory
  460.  
  461. END_OF_USAGE
  462.  
  463. sub parse_command_line {
  464.     my ($opt_flush,$opt_help,$opt_htmlroot,$opt_index,$opt_infile,$opt_libpods,$opt_netscape,$opt_outfile,$opt_podpath,$opt_podroot,$opt_norecurse,$opt_recurse,$opt_title,$opt_verbose);
  465.     my $result = GetOptions(
  466.                 'flush'      => \$opt_flush,
  467.                 'help'       => \$opt_help,
  468.                 'htmlroot=s' => \$opt_htmlroot,
  469.                 'index!'     => \$opt_index,
  470.                 'infile=s'   => \$opt_infile,
  471.                 'libpods=s'  => \$opt_libpods,
  472.                 'netscape!'  => \$opt_netscape,
  473.                 'outfile=s'  => \$opt_outfile,
  474.                 'podpath=s'  => \$opt_podpath,
  475.                 'podroot=s'  => \$opt_podroot,
  476.                 'norecurse'  => \$opt_norecurse,
  477.                 'recurse!'   => \$opt_recurse,
  478.                 'title=s'    => \$opt_title,
  479.                 'verbose'    => \$opt_verbose,
  480.                );
  481.     usage("-", "invalid parameters") if not $result;
  482.  
  483.     usage("-") if defined $opt_help;    # see if the user asked for help
  484.     $opt_help = "";            # just to make -w shut-up.
  485.  
  486.     $podfile  = $opt_infile if defined $opt_infile;
  487.     $htmlfile = $opt_outfile if defined $opt_outfile;
  488.  
  489.     @podpath  = split(":", $opt_podpath) if defined $opt_podpath;
  490.     @libpods  = split(":", $opt_libpods) if defined $opt_libpods;
  491.  
  492.     warn "Flushing item and directory caches\n"
  493.     if $opt_verbose && defined $opt_flush;
  494.     unlink($dircache, $itemcache) if defined $opt_flush;
  495.  
  496.     $htmlroot = $opt_htmlroot if defined $opt_htmlroot;
  497.     $podroot  = $opt_podroot if defined $opt_podroot;
  498.  
  499.     $doindex  = $opt_index if defined $opt_index;
  500.     $recurse  = $opt_recurse if defined $opt_recurse;
  501.     $title    = $opt_title if defined $opt_title;
  502.     $verbose  = defined $opt_verbose ? 1 : 0;
  503.     $netscape = $opt_netscape if defined $opt_netscape;
  504. }
  505.  
  506.  
  507. my $saved_cache_key;
  508.  
  509. sub get_cache {
  510.     my($dircache, $itemcache, $podpath, $podroot, $recurse) = @_;
  511.     my @cache_key_args = @_;
  512.  
  513.     # A first-level cache:
  514.     # Don't bother reading the cache files if they still apply
  515.     # and haven't changed since we last read them.
  516.  
  517.     my $this_cache_key = cache_key(@cache_key_args);
  518.  
  519.     return if $saved_cache_key and $this_cache_key eq $saved_cache_key;
  520.  
  521.     # load the cache of %pages and %items if possible.  $tests will be
  522.     # non-zero if successful.
  523.     my $tests = 0;
  524.     if (-f $dircache && -f $itemcache) {
  525.     warn "scanning for item cache\n" if $verbose;
  526.     $tests = load_cache($dircache, $itemcache, $podpath, $podroot);
  527.     }
  528.  
  529.     # if we didn't succeed in loading the cache then we must (re)build
  530.     #  %pages and %items.
  531.     if (!$tests) {
  532.     warn "scanning directories in pod-path\n" if $verbose;
  533.     scan_podpath($podroot, $recurse, 0);
  534.     }
  535.     $saved_cache_key = cache_key(@cache_key_args);
  536. }
  537.  
  538. sub cache_key {
  539.     my($dircache, $itemcache, $podpath, $podroot, $recurse) = @_;
  540.     return join('!', $dircache, $itemcache, $recurse,
  541.         @$podpath, $podroot, stat($dircache), stat($itemcache));
  542. }
  543.  
  544. #
  545. # load_cache - tries to find if the caches stored in $dircache and $itemcache
  546. #  are valid caches of %pages and %items.  if they are valid then it loads
  547. #  them and returns a non-zero value.
  548. #
  549.  
  550. sub load_cache {
  551.     my($dircache, $itemcache, $podpath, $podroot) = @_;
  552.     my($tests);
  553.     local $_;
  554.  
  555.     $tests = 0;
  556.  
  557.     open(CACHE, "<$itemcache") ||
  558.     die "$0: error opening $itemcache for reading: $!\n";
  559.     $/ = "\n";
  560.  
  561.     # is it the same podpath?
  562.     $_ = <CACHE>;
  563.     chomp($_);
  564.     $tests++ if (join(":", @$podpath) eq $_);
  565.  
  566.     # is it the same podroot?
  567.     $_ = <CACHE>;
  568.     chomp($_);
  569.     $tests++ if ($podroot eq $_);
  570.  
  571.     # load the cache if its good
  572.     if ($tests != 2) {
  573.     close(CACHE);
  574.     return 0;
  575.     }
  576.  
  577.     warn "loading item cache\n" if $verbose;
  578.     while (<CACHE>) {
  579.     /(.*?) (.*)$/;
  580.     $items{$1} = $2;
  581.     }
  582.     close(CACHE);
  583.  
  584.     warn "scanning for directory cache\n" if $verbose;
  585.     open(CACHE, "<$dircache") ||
  586.     die "$0: error opening $dircache for reading: $!\n";
  587.     $/ = "\n";
  588.     $tests = 0;
  589.  
  590.     # is it the same podpath?
  591.     $_ = <CACHE>;
  592.     chomp($_);
  593.     $tests++ if (join(":", @$podpath) eq $_);
  594.  
  595.     # is it the same podroot?
  596.     $_ = <CACHE>;
  597.     chomp($_);
  598.     $tests++ if ($podroot eq $_);
  599.  
  600.     # load the cache if its good
  601.     if ($tests != 2) {
  602.     close(CACHE);
  603.     return 0;
  604.     }
  605.  
  606.     warn "loading directory cache\n" if $verbose;
  607.     while (<CACHE>) {
  608.     /(.*?) (.*)$/;
  609.     $pages{$1} = $2;
  610.     }
  611.  
  612.     close(CACHE);
  613.  
  614.     return 1;
  615. }
  616.  
  617. #
  618. # scan_podpath - scans the directories specified in @podpath for directories,
  619. #  .pod files, and .pm files.  it also scans the pod files specified in
  620. #  @libpods for =item directives.
  621. #
  622. sub scan_podpath {
  623.     my($podroot, $recurse, $append) = @_;
  624.     my($pwd, $dir);
  625.     my($libpod, $dirname, $pod, @files, @poddata);
  626.  
  627.     unless($append) {
  628.     %items = ();
  629.     %pages = ();
  630.     }
  631.  
  632.     # scan each directory listed in @podpath
  633.     $pwd = getcwd();
  634.     chdir($podroot)
  635.     || die "$0: error changing to directory $podroot: $!\n";
  636.     foreach $dir (@podpath) {
  637.     scan_dir($dir, $recurse);
  638.     }
  639.  
  640.     # scan the pods listed in @libpods for =item directives
  641.     foreach $libpod (@libpods) {
  642.     # if the page isn't defined then we won't know where to find it
  643.     # on the system.
  644.     next unless defined $pages{$libpod} && $pages{$libpod};
  645.  
  646.     # if there is a directory then use the .pod and .pm files within it.
  647.     if ($pages{$libpod} =~ /([^:]*[^(\.pod|\.pm)]):/) {
  648.         #  find all the .pod and .pm files within the directory
  649.         $dirname = $1;
  650.         opendir(DIR, $dirname) ||
  651.         die "$0: error opening directory $dirname: $!\n";
  652.         @files = grep(/(\.pod|\.pm)$/ && ! -d $_, readdir(DIR));
  653.         closedir(DIR);
  654.  
  655.         # scan each .pod and .pm file for =item directives
  656.         foreach $pod (@files) {
  657.         open(POD, "<$dirname/$pod") ||
  658.             die "$0: error opening $dirname/$pod for input: $!\n";
  659.         @poddata = <POD>;
  660.         close(POD);
  661.  
  662.         scan_items("$dirname/$pod", @poddata);
  663.         }
  664.  
  665.         # use the names of files as =item directives too.
  666.         foreach $pod (@files) {
  667.         $pod =~ /^(.*)(\.pod|\.pm)$/;
  668.         $items{$1} = "$dirname/$1.html" if $1;
  669.         }
  670.     } elsif ($pages{$libpod} =~ /([^:]*\.pod):/ ||
  671.          $pages{$libpod} =~ /([^:]*\.pm):/) {
  672.         # scan the .pod or .pm file for =item directives
  673.         $pod = $1;
  674.         open(POD, "<$pod") ||
  675.         die "$0: error opening $pod for input: $!\n";
  676.         @poddata = <POD>;
  677.         close(POD);
  678.  
  679.         scan_items("$pod", @poddata);
  680.     } else {
  681.         warn "$0: shouldn't be here (line ".__LINE__."\n";
  682.     }
  683.     }
  684.     @poddata = ();    # clean-up a bit
  685.  
  686.     chdir($pwd)
  687.     || die "$0: error changing to directory $pwd: $!\n";
  688.  
  689.     # cache the item list for later use
  690.     warn "caching items for later use\n" if $verbose;
  691.     open(CACHE, ">$itemcache") ||
  692.     die "$0: error open $itemcache for writing: $!\n";
  693.  
  694.     print CACHE join(":", @podpath) . "\n$podroot\n";
  695.     foreach my $key (keys %items) {
  696.     print CACHE "$key $items{$key}\n";
  697.     }
  698.  
  699.     close(CACHE);
  700.  
  701.     # cache the directory list for later use
  702.     warn "caching directories for later use\n" if $verbose;
  703.     open(CACHE, ">$dircache") ||
  704.     die "$0: error open $dircache for writing: $!\n";
  705.  
  706.     print CACHE join(":", @podpath) . "\n$podroot\n";
  707.     foreach my $key (keys %pages) {
  708.     print CACHE "$key $pages{$key}\n";
  709.     }
  710.  
  711.     close(CACHE);
  712. }
  713.  
  714. #
  715. # scan_dir - scans the directory specified in $dir for subdirectories, .pod
  716. #  files, and .pm files.  notes those that it finds.  this information will
  717. #  be used later in order to figure out where the pages specified in L<>
  718. #  links are on the filesystem.
  719. #
  720. sub scan_dir {
  721.     my($dir, $recurse) = @_;
  722.     my($t, @subdirs, @pods, $pod, $dirname, @dirs);
  723.     local $_;
  724.  
  725.     @subdirs = ();
  726.     @pods = ();
  727.  
  728.     opendir(DIR, $dir) ||
  729.     die "$0: error opening directory $dir: $!\n";
  730.     while (defined($_ = readdir(DIR))) {
  731.     if (-d "$dir/$_" && $_ ne "." && $_ ne "..") {        # directory
  732.         $pages{$_}  = "" unless defined $pages{$_};
  733.         $pages{$_} .= "$dir/$_:";
  734.         push(@subdirs, $_);
  735.     } elsif (/\.pod$/) {                                # .pod
  736.         s/\.pod$//;
  737.         $pages{$_}  = "" unless defined $pages{$_};
  738.         $pages{$_} .= "$dir/$_.pod:";
  739.         push(@pods, "$dir/$_.pod");
  740.     } elsif (/\.pm$/) {                                 # .pm
  741.         s/\.pm$//;
  742.         $pages{$_}  = "" unless defined $pages{$_};
  743.         $pages{$_} .= "$dir/$_.pm:";
  744.         push(@pods, "$dir/$_.pm");
  745.     }
  746.     }
  747.     closedir(DIR);
  748.  
  749.     # recurse on the subdirectories if necessary
  750.     if ($recurse) {
  751.     foreach my $subdir (@subdirs) {
  752.         scan_dir("$dir/$subdir", $recurse);
  753.     }
  754.     }
  755. }
  756.  
  757. #
  758. # scan_headings - scan a pod file for head[1-6] tags, note the tags, and
  759. #  build an index.
  760. #
  761. sub scan_headings {
  762.     my($sections, @data) = @_;
  763.     my($tag, $which_head, $title, $listdepth, $index);
  764.  
  765.     # here we need    local $ignore = 0;
  766.     #  unfortunately, we can't have it, because $ignore is lexical
  767.     $ignore = 0;
  768.  
  769.     $listdepth = 0;
  770.     $index = "";
  771.  
  772.     # scan for =head directives, note their name, and build an index
  773.     #  pointing to each of them.
  774.     foreach my $line (@data) {
  775.     if ($line =~ /^=(head)([1-6])\s+(.*)/) {
  776.         ($tag,$which_head, $title) = ($1,$2,$3);
  777.         chomp($title);
  778.         $$sections{htmlify(0,$title)} = 1;
  779.  
  780.         while ($which_head != $listdepth) {
  781.         if ($which_head > $listdepth) {
  782.             $index .= "\n" . ("\t" x $listdepth) . "<UL>\n";
  783.             $listdepth++;
  784.         } elsif ($which_head < $listdepth) {
  785.             $listdepth--;
  786.             $index .= "\n" . ("\t" x $listdepth) . "</UL>\n";
  787.         }
  788.         }
  789.  
  790.         $index .= "\n" . ("\t" x $listdepth) . "<LI>" .
  791.                   "<A HREF=\"#" . htmlify(0,$title) . "\">" .
  792.               html_escape(process_text(\$title, 0)) . "</A>";
  793.     }
  794.     }
  795.  
  796.     # finish off the lists
  797.     while ($listdepth--) {
  798.     $index .= "\n" . ("\t" x $listdepth) . "</UL>\n";
  799.     }
  800.  
  801.     # get rid of bogus lists
  802.     $index =~ s,\t*<UL>\s*</UL>\n,,g;
  803.  
  804.     $ignore = 1;    # restore old value;
  805.  
  806.     return $index;
  807. }
  808.  
  809. #
  810. # scan_items - scans the pod specified by $pod for =item directives.  we
  811. #  will use this information later on in resolving C<> links.
  812. #
  813. sub scan_items {
  814.     my($pod, @poddata) = @_;
  815.     my($i, $item);
  816.     local $_;
  817.  
  818.     $pod =~ s/\.pod$//;
  819.     $pod .= ".html" if $pod;
  820.  
  821.     foreach $i (0..$#poddata) {
  822.     $_ = $poddata[$i];
  823.  
  824.     # remove any formatting instructions
  825.     s,[A-Z]<([^<>]*)>,$1,g;
  826.  
  827.     # figure out what kind of item it is and get the first word of
  828.     #  it's name.
  829.     if (/^=item\s+(\w*)\s*.*$/s) {
  830.         if ($1 eq "*") {        # bullet list
  831.         /\A=item\s+\*\s*(.*?)\s*\Z/s;
  832.         $item = $1;
  833.         } elsif ($1 =~ /^\d+/) {    # numbered list
  834.         /\A=item\s+\d+\.?(.*?)\s*\Z/s;
  835.         $item = $1;
  836.         } else {
  837. #        /\A=item\s+(.*?)\s*\Z/s;
  838.         /\A=item\s+(\w*)/s;
  839.         $item = $1;
  840.         }
  841.  
  842.         $items{$item} = "$pod" if $item;
  843.     }
  844.     }
  845. }
  846.  
  847. #
  848. # process_head - convert a pod head[1-6] tag and convert it to HTML format.
  849. #
  850. sub process_head {
  851.     my($tag, $heading) = @_;
  852.     my $firstword;
  853.  
  854.     # figure out the level of the =head
  855.     $tag =~ /head([1-6])/;
  856.     my $level = $1;
  857.  
  858.     # can't have a heading full of spaces and speechmarks and so on
  859.     $firstword = $heading; $firstword =~ s/\s*(\w+)\s.*/$1/;
  860.  
  861.     print HTML "<P>\n" unless $listlevel;
  862.     print HTML "<HR>\n" unless $listlevel || $top;
  863.     print HTML "<H$level>"; # unless $listlevel;
  864.     #print HTML "<H$level>" unless $listlevel;
  865.     my $convert = $heading; process_text(\$convert, 0);
  866.     $convert = html_escape($convert);
  867.     print HTML '<A NAME="' . htmlify(0,$heading) . "\">$convert</A>";
  868.     print HTML "</H$level>"; # unless $listlevel;
  869.     print HTML "\n";
  870. }
  871.  
  872. #
  873. # process_item - convert a pod item tag and convert it to HTML format.
  874. #
  875. sub process_item {
  876.     my $text = $_[0];
  877.     my($i, $quote, $name);
  878.  
  879.     my $need_preamble = 0;
  880.     my $this_entry;
  881.  
  882.  
  883.     # lots of documents start a list without doing an =over.  this is
  884.     # bad!  but, the proper thing to do seems to be to just assume
  885.     # they did do an =over.  so warn them once and then continue.
  886.     warn "$0: $podfile: unexpected =item directive in paragraph $paragraph.  ignoring.\n"
  887.     unless $listlevel;
  888.     process_over() unless $listlevel;
  889.  
  890.     return unless $listlevel;
  891.  
  892.     # remove formatting instructions from the text
  893.     1 while $text =~ s/[A-Z]<([^<>]*)>/$1/g;
  894.     pre_escape(\$text);
  895.  
  896.     $need_preamble = $items_seen[$listlevel]++ == 0;
  897.  
  898.     # check if this is the first =item after an =over
  899.     $i = $listlevel - 1;
  900.     my $need_new = $listlevel >= @listitem;
  901.  
  902.     if ($text =~ /\A\*/) {        # bullet
  903.  
  904.     if ($need_preamble) {
  905.         push(@listend,  "</UL>");
  906.         print HTML "<UL>\n";
  907.     }
  908.  
  909.     print HTML '<LI>';
  910.     if ($text =~ /\A\*\s*(.+)\Z/s) {
  911.         print HTML '<STRONG>';
  912.         if ($items_named{$1}++) {
  913.         print HTML html_escape($1);
  914.         } else {
  915.         my $name = 'item_' . htmlify(1,$1);
  916.         print HTML qq(<A NAME="$name">), html_escape($1), '</A>';
  917.         }
  918.         print HTML '</STRONG>';
  919.     }
  920.  
  921.     } elsif ($text =~ /\A[\d#]+/) {    # numbered list
  922.  
  923.     if ($need_preamble) {
  924.         push(@listend,  "</OL>");
  925.         print HTML "<OL>\n";
  926.     }
  927.  
  928.     print HTML '<LI>';
  929.     if ($text =~ /\A\d+\.?\s*(.+)\Z/s) {
  930.         print HTML '<STRONG>';
  931.         if ($items_named{$1}++) {
  932.         print HTML html_escape($1);
  933.         } else {
  934.         my $name = 'item_' . htmlify(0,$1);
  935.         print HTML qq(<A NAME="$name">), html_escape($1), '</A>';
  936.         }
  937.         print HTML '</STRONG>';
  938.     }
  939.  
  940.     } else {            # all others
  941.  
  942.     if ($need_preamble) {
  943.         push(@listend,  '</DL>');
  944.         print HTML "<DL>\n";
  945.     }
  946.  
  947.     print HTML '<DT>';
  948.     if ($text =~ /(\S+)/) {
  949.         print HTML '<STRONG>';
  950.         if ($items_named{$1}++) {
  951.         print HTML html_escape($text);
  952.         } else {
  953.         my $name = 'item_' . htmlify(1,$text);
  954.         print HTML qq(<A NAME="$name">), html_escape($text), '</A>';
  955.         }
  956.         print HTML '</STRONG>';
  957.     }
  958.        print HTML '<DD>';
  959.     }
  960.  
  961.     print HTML "\n";
  962. }
  963.  
  964. #
  965. # process_over - process a pod over tag and start a corresponding HTML
  966. # list.
  967. #
  968. sub process_over {
  969.     # start a new list
  970.     $listlevel++;
  971. }
  972.  
  973. #
  974. # process_back - process a pod back tag and convert it to HTML format.
  975. #
  976. sub process_back {
  977.     warn "$0: $podfile: unexpected =back directive in paragraph $paragraph.  ignoring.\n"
  978.     unless $listlevel;
  979.     return unless $listlevel;
  980.  
  981.     # close off the list.  note, I check to see if $listend[$listlevel] is
  982.     # defined because an =item directive may have never appeared and thus
  983.     # $listend[$listlevel] may have never been initialized.
  984.     $listlevel--;
  985.     print HTML $listend[$listlevel] if defined $listend[$listlevel];
  986.     print HTML "\n";
  987.  
  988.     # don't need the corresponding perl code anymore
  989.     pop(@listitem);
  990.     pop(@listdata);
  991.     pop(@listend);
  992.  
  993.     pop(@items_seen);
  994. }
  995.  
  996. #
  997. # process_cut - process a pod cut tag, thus stop ignoring pod directives.
  998. #
  999. sub process_cut {
  1000.     $ignore = 1;
  1001. }
  1002.  
  1003. #
  1004. # process_pod - process a pod pod tag, thus ignore pod directives until we see a
  1005. # corresponding cut.
  1006. #
  1007. sub process_pod {
  1008.     # no need to set $ignore to 0 cause the main loop did it
  1009. }
  1010.  
  1011. #
  1012. # process_for - process a =for pod tag.  if it's for html, split
  1013. # it out verbatim, if illustration, center it, otherwise ignore it.
  1014. #
  1015. sub process_for {
  1016.     my($whom, $text) = @_;
  1017.     if ( $whom =~ /^(pod2)?html$/i) {
  1018.     print HTML $text;
  1019.     } elsif ($whom =~ /^illustration$/i) {
  1020.         1 while chomp $text;
  1021.     for my $ext (qw[.png .gif .jpeg .jpg .tga .pcl .bmp]) {
  1022.       $text .= $ext, last if -r "$text$ext";
  1023.     }
  1024.         print HTML qq{<p align = "center"><img src = "$text" alt = "$text illustration"></p>};
  1025.     }
  1026. }
  1027.  
  1028. #
  1029. # process_begin - process a =begin pod tag.  this pushes
  1030. # whom we're beginning on the begin stack.  if there's a
  1031. # begin stack, we only print if it us.
  1032. #
  1033. sub process_begin {
  1034.     my($whom, $text) = @_;
  1035.     $whom = lc($whom);
  1036.     push (@begin_stack, $whom);
  1037.     if ( $whom =~ /^(pod2)?html$/) {
  1038.     print HTML $text if $text;
  1039.     }
  1040. }
  1041.  
  1042. #
  1043. # process_end - process a =end pod tag.  pop the
  1044. # begin stack.  die if we're mismatched.
  1045. #
  1046. sub process_end {
  1047.     my($whom, $text) = @_;
  1048.     $whom = lc($whom);
  1049.     if ($begin_stack[-1] ne $whom ) {
  1050.     die "Unmatched begin/end at chunk $paragraph\n"
  1051.     } 
  1052.     pop @begin_stack;
  1053. }
  1054.  
  1055. #
  1056. # process_text - handles plaintext that appears in the input pod file.
  1057. # there may be pod commands embedded within the text so those must be
  1058. # converted to html commands.
  1059. #
  1060. sub process_text {
  1061.     my($text, $escapeQuotes) = @_;
  1062.     my($result, $rest, $s1, $s2, $s3, $s4, $match, $bf);
  1063.     my($podcommand, $params, $tag, $quote);
  1064.  
  1065.     return if $ignore;
  1066.  
  1067.     $quote  = 0;                # status of double-quote conversion
  1068.     $result = "";
  1069.     $rest = $$text;
  1070.  
  1071.     if ($rest =~ /^\s+/) {    # preformatted text, no pod directives
  1072.     $rest =~ s/\n+\Z//;
  1073.     $rest =~ s#.*#
  1074.         my $line = $&;
  1075.         1 while $line =~ s/\t+/' ' x (length($&) * 8 - length($`) % 8)/e;
  1076.         $line;
  1077.     #eg;
  1078.  
  1079.     $rest   =~ s/&/&/g;
  1080.     $rest   =~ s/</</g;
  1081.     $rest   =~ s/>/>/g;
  1082.     $rest   =~ s/"/"/g;
  1083.  
  1084.     # try and create links for all occurrences of perl.* within
  1085.     # the preformatted text.
  1086.     $rest =~ s{
  1087.             (\s*)(perl\w+)
  1088.           }{
  1089.             if (defined $pages{$2}) {    # is a link
  1090.             qq($1<A HREF="$htmlroot/$pages{$2}">$2</A>);
  1091.             } elsif (defined $pages{dosify($2)}) {    # is a link
  1092.             qq($1<A HREF="$htmlroot/$pages{dosify($2)}">$2</A>);
  1093.             } else {
  1094.             "$1$2";
  1095.             }
  1096.           }xeg;
  1097.     $rest =~ s/(<A HREF=)([^>:]*:)?([^>:]*)\.pod:([^>:]*:)?/$1$3.html/g;
  1098.  
  1099.   my $urls = '(' . join ('|', qw{
  1100.                 http
  1101.                 telnet
  1102.         mailto
  1103.         news
  1104.                 gopher
  1105.                 file
  1106.                 wais
  1107.                 ftp
  1108.             } ) 
  1109.         . ')';
  1110.   
  1111.   my $ltrs = '\w';
  1112.   my $gunk = '/#~:.?+=&%@!\-';
  1113.   my $punc = '.:?\-';
  1114.   my $any  = "${ltrs}${gunk}${punc}";
  1115.  
  1116.   $rest =~ s{
  1117.         \b                          # start at word boundary
  1118.         (                           # begin $1  {
  1119.           $urls     :               # need resource and a colon
  1120.           [$any] +?                 # followed by on or more
  1121.                                     #  of any valid character, but
  1122.                                     #  be conservative and take only
  1123.                                     #  what you need to....
  1124.         )                           # end   $1  }
  1125.         (?=                         # look-ahead non-consumptive assertion
  1126.                 [$punc]*            # either 0 or more puntuation
  1127.                 [^$any]             #   followed by a non-url char
  1128.             |                       # or else
  1129.                 $                   #   then end of the string
  1130.         )
  1131.       }{<A HREF="$1">$1</A>}igox;
  1132.  
  1133.     $result =   "<PRE>"    # text should be as it is (verbatim)
  1134.           . "$rest\n"
  1135.           . "</PRE>\n";
  1136.     } else {            # formatted text
  1137.     # parse through the string, stopping each time we find a
  1138.     # pod-escape.  once the string has been throughly processed
  1139.     # we can output it.
  1140.     while (length $rest) {
  1141.         # check to see if there are any possible pod directives in
  1142.         # the remaining part of the text.
  1143.         if ($rest =~ m/[BCEIFLSZ]</) {
  1144.         warn "\$rest\t= $rest\n" unless
  1145.             $rest =~ /\A
  1146.                ([^<]*?)
  1147.                ([BCEIFLSZ]?)
  1148.                <
  1149.                (.*)\Z/xs;
  1150.  
  1151.         $s1 = $1;    # pure text
  1152.         $s2 = $2;    # the type of pod-escape that follows
  1153.         $s3 = '<';    # '<'
  1154.         $s4 = $3;    # the rest of the string
  1155.         } else {
  1156.         $s1 = $rest;
  1157.         $s2 = "";
  1158.         $s3 = "";
  1159.         $s4 = "";
  1160.         }
  1161.  
  1162.         if ($s3 eq '<' && $s2) {    # a pod-escape
  1163.         $result    .= ($escapeQuotes ? process_puretext($s1, \$quote) : $s1);
  1164.         $podcommand = "$s2<";
  1165.         $rest       = $s4;
  1166.  
  1167.         # find the matching '>'
  1168.         $match = 1;
  1169.         $bf = 0;
  1170.         while ($match && !$bf) {
  1171.             $bf = 1;
  1172.             if ($rest =~ /\A([^<>]*[BCEIFLSZ]<)(.*)\Z/s) {
  1173.             $bf = 0;
  1174.             $match++;
  1175.             $podcommand .= $1;
  1176.             $rest        = $2;
  1177.             } elsif ($rest =~ /\A([^>]*>)(.*)\Z/s) {
  1178.             $bf = 0;
  1179.             $match--;
  1180.             $podcommand .= $1;
  1181.             $rest        = $2;
  1182.             }
  1183.         }
  1184.  
  1185.         if ($match != 0) {
  1186.             warn <<WARN;
  1187. $0: $podfile: cannot find matching > for $s2 in paragraph $paragraph.
  1188. WARN
  1189.             $result .= substr $podcommand, 0, 2;
  1190.             $rest = substr($podcommand, 2) . $rest;
  1191.             next;
  1192.         }
  1193.  
  1194.         # pull out the parameters to the pod-escape
  1195.         $podcommand =~ /^([BCFEILSZ]?)<(.*)>$/s;
  1196.         $tag    = $1;
  1197.         $params = $2;
  1198.  
  1199.         # process the text within the pod-escape so that any escapes
  1200.         # which must occur do.
  1201.         process_text(\$params, 0) unless $tag eq 'L';
  1202.  
  1203.         $s1 = $params;
  1204.         if (!$tag || $tag eq " ") {    #  <> : no tag
  1205.             $s1 = "<$params>";
  1206.         } elsif ($tag eq "L") {        # L<> : link 
  1207.             $s1 = process_L($params);
  1208.         } elsif ($tag eq "I" ||        # I<> : italicize text
  1209.              $tag eq "B" ||        # B<> : bold text
  1210.              $tag eq "F") {        # F<> : file specification
  1211.             $s1 = process_BFI($tag, $params);
  1212.         } elsif ($tag eq "C") {        # C<> : literal code
  1213.             $s1 = process_C($params, 1);
  1214.         } elsif ($tag eq "E") {        # E<> : escape
  1215.             $s1 = process_E($params);
  1216.         } elsif ($tag eq "Z") {        # Z<> : zero-width character
  1217.             $s1 = process_Z($params);
  1218.         } elsif ($tag eq "S") {        # S<> : non-breaking space
  1219.             $s1 = process_S($params);
  1220.         } elsif ($tag eq "X") {        # S<> : non-breaking space
  1221.             $s1 = process_X($params);
  1222.         } else {
  1223.             warn "$0: $podfile: unhandled tag '$tag' in paragraph $paragraph\n";
  1224.         }
  1225.  
  1226.         $result .= "$s1";
  1227.         } else {
  1228.         # for pure text we must deal with implicit links and
  1229.         # double-quotes among other things.
  1230.         $result .= ($escapeQuotes ? process_puretext("$s1$s2$s3", \$quote) : "$s1$s2$s3");
  1231.         $rest    = $s4;
  1232.         }
  1233.     }
  1234.     }
  1235.     $$text = $result;
  1236. }
  1237.  
  1238. sub html_escape {
  1239.     my $rest = $_[0];
  1240.     $rest   =~ s/&/&/g;
  1241.     $rest   =~ s/</</g;
  1242.     $rest   =~ s/>/>/g;
  1243.     $rest   =~ s/"/"/g;
  1244.     return $rest;
  1245.  
  1246. #
  1247. # process_puretext - process pure text (without pod-escapes) converting
  1248. #  double-quotes and handling implicit C<> links.
  1249. #
  1250. sub process_puretext {
  1251.     my($text, $quote) = @_;
  1252.     my(@words, $result, $rest, $lead, $trail);
  1253.  
  1254.     # convert double-quotes to single-quotes
  1255.     $text =~ s/\A([^"]*)"/$1''/s if $$quote;
  1256.     while ($text =~ s/\A([^"]*)["]([^"]*)["]/$1``$2''/sg) {}
  1257.  
  1258.     $$quote = ($text =~ m/"/ ? 1 : 0);
  1259.     $text =~ s/\A([^"]*)"/$1``/s if $$quote;
  1260.  
  1261.     # keep track of leading and trailing white-space
  1262.     $lead  = ($text =~ /\A(\s*)/s ? $1 : "");
  1263.     $trail = ($text =~ /(\s*)\Z/s ? $1 : "");
  1264.  
  1265.     # collapse all white space into a single space
  1266.     $text =~ s/\s+/ /g;
  1267.     @words = split(" ", $text);
  1268.  
  1269.     # process each word individually
  1270.     foreach my $word (@words) {
  1271.     # see if we can infer a link
  1272.     if ($word =~ /^\w+\(/) {
  1273.         # has parenthesis so should have been a C<> ref
  1274.         $word = process_C($word);
  1275. #        $word =~ /^[^()]*]\(/;
  1276. #        if (defined $items{$1} && $items{$1}) {
  1277. #        $word =   "\n<CODE><A HREF=\"$htmlroot/$items{$1}#item_"
  1278. #            . htmlify(0,$word)
  1279. #            . "\">$word</A></CODE>";
  1280. #        } elsif (defined $items{$word} && $items{$word}) {
  1281. #        $word =   "\n<CODE><A HREF=\"$htmlroot/$items{$word}#item_"
  1282. #            . htmlify(0,$word)
  1283. #            . "\">$word</A></CODE>";
  1284. #        } else {
  1285. #        $word =   "\n<CODE><A HREF=\"#item_"
  1286. #            . htmlify(0,$word)
  1287. #            . "\">$word</A></CODE>";
  1288. #        }
  1289.     } elsif ($word =~ /^[\$\@%&*]+\w+$/) {
  1290.         # perl variables, should be a C<> ref
  1291.         $word = process_C($word, 1);
  1292.     } elsif ($word =~ m,^\w+://\w,) {
  1293.         # looks like a URL
  1294.         $word = qq(<A HREF="$word">$word</A>);
  1295.     } elsif ($word =~ /[\w.-]+\@[\w-]+\.\w/) {
  1296.         # looks like an e-mail address
  1297.         my ($w1, $w2, $w3) = ("", $word, "");
  1298.         ($w1, $w2, $w3) = ("(", $1, ")$2") if $word =~ /^\((.*?)\)(,?)/;
  1299.         ($w1, $w2, $w3) = ("<", $1, ">$2") if $word =~ /^<(.*?)>(,?)/;
  1300.         $word = qq($w1<A HREF="mailto:$w2">$w2</A>$w3);
  1301.     } elsif ($word !~ /[a-z]/ && $word =~ /[A-Z]/) {  # all uppercase?
  1302.         $word = html_escape($word) if $word =~ /["&<>]/;
  1303.         $word = "\n<FONT SIZE=-1>$word</FONT>" if $netscape;
  1304.     } else { 
  1305.         $word = html_escape($word) if $word =~ /["&<>]/;
  1306.     }
  1307.     }
  1308.  
  1309.     # build a new string based upon our conversion
  1310.     $result = "";
  1311.     $rest   = join(" ", @words);
  1312.     while (length($rest) > 75) {
  1313.     if ( $rest =~ m/^(.{0,75})\s(.*?)$/o ||
  1314.          $rest =~ m/^(\S*)\s(.*?)$/o) {
  1315.  
  1316.         $result .= "$1\n";
  1317.         $rest    = $2;
  1318.     } else {
  1319.         $result .= "$rest\n";
  1320.         $rest    = "";
  1321.     }
  1322.     }
  1323.     $result .= $rest if $rest;
  1324.  
  1325.     # restore the leading and trailing white-space
  1326.     $result = "$lead$result$trail";
  1327.  
  1328.     return $result;
  1329. }
  1330.  
  1331. #
  1332. # pre_escape - convert & in text to $amp;
  1333. #
  1334. sub pre_escape {
  1335.     my($str) = @_;
  1336.  
  1337.     $$str =~ s,&,&,g;
  1338. }
  1339.  
  1340. #
  1341. # dosify - convert filenames to 8.3
  1342. #
  1343. sub dosify {
  1344.     my($str) = @_;
  1345.     if ($Is83) {
  1346.         $str = lc $str;
  1347.         $str =~ s/(\.\w+)/substr ($1,0,4)/ge;
  1348.         $str =~ s/(\w+)/substr ($1,0,8)/ge;
  1349.     }
  1350.     return $str;
  1351. }
  1352.  
  1353. #
  1354. # process_L - convert a pod L<> directive to a corresponding HTML link.
  1355. #  most of the links made are inferred rather than known about directly
  1356. #  (i.e it's not known whether the =head\d section exists in the target file,
  1357. #   or whether a .pod file exists in the case of split files).  however, the
  1358. #  guessing usually works.
  1359. #
  1360. # Unlike the other directives, this should be called with an unprocessed
  1361. # string, else tags in the link won't be matched.
  1362. #
  1363. sub process_L {
  1364.     my($str) = @_;
  1365.     my($s1, $s2, $linktext, $page, $page83, $section, $link);    # work strings
  1366.  
  1367.     $str =~ s/\n/ /g;            # undo word-wrapped tags
  1368.     $s1 = $str;
  1369.     for ($s1) {
  1370.     # LREF: a la HREF L<show this text|man/section>
  1371.     $linktext = $1 if s:^([^|]+)\|::;
  1372.  
  1373.     # a :: acts like a /
  1374.     s,::,/,;
  1375.  
  1376.     # make sure sections start with a /
  1377.     s,^",/",g;
  1378.     s,^,/,g if (!m,/, && / /);
  1379.  
  1380.     # check if there's a section specified
  1381.     if (m,^(.*?)/"?(.*?)"?$,) {    # yes
  1382.         ($page, $section) = ($1, $2);
  1383.     } else {            # no
  1384.         ($page, $section) = ($str, "");
  1385.     }
  1386.  
  1387.     # check if we know that this is a section in this page
  1388.     if (!defined $pages{$page} && defined $sections{$page}) {
  1389.         $section = $page;
  1390.         $page = "";
  1391.     }
  1392.     }
  1393.  
  1394.     $page83=dosify($page);
  1395.     $page=$page83 if (defined $pages{$page83});
  1396.     if ($page eq "") {
  1397.     $link = "#" . htmlify(0,$section);
  1398.     $linktext = $section unless defined($linktext);
  1399.     } elsif (!defined $pages{$page}) {
  1400.     warn "$0: $podfile: cannot resolve L<$str> in paragraph $paragraph: no such page '$page'\n";
  1401.     $link = "";
  1402.     $linktext = $page unless defined($linktext);
  1403.     } else {
  1404.     $linktext  = ($section ? "$section" : "the $page manpage") unless defined($linktext);
  1405.     $section = htmlify(0,$section) if $section ne "";
  1406.  
  1407.     # if there is a directory by the name of the page, then assume that an
  1408.     # appropriate section will exist in the subdirectory
  1409.     if ($section ne "" && $pages{$page} =~ /([^:]*[^(\.pod|\.pm)]):/) {
  1410.         $link = "$htmlroot/$1/$section.html";
  1411.  
  1412.     # since there is no directory by the name of the page, the section will
  1413.     # have to exist within a .html of the same name.  thus, make sure there
  1414.     # is a .pod or .pm that might become that .html
  1415.     } else {
  1416.         $section = "#$section";
  1417.         # check if there is a .pod with the page name
  1418.         if ($pages{$page} =~ /([^:]*)\.pod:/) {
  1419.         $link = "$htmlroot/$1.html$section";
  1420.         } elsif ($pages{$page} =~ /([^:]*)\.pm:/) {
  1421.         $link = "$htmlroot/$1.html$section";
  1422.         } else {
  1423.         warn "$0: $podfile: cannot resolve L$str in paragraph $paragraph: ".
  1424.                  "no .pod or .pm found\n";
  1425.         $link = "";
  1426.         $linktext = $section unless defined($linktext);
  1427.         }
  1428.     }
  1429.     }
  1430.  
  1431.     process_text(\$linktext, 0);
  1432.     if ($link) {
  1433.     $s1 = "<A HREF=\"$link\">$linktext</A>";
  1434.     } else {
  1435.     $s1 = "<EM>$linktext</EM>";
  1436.     }
  1437.     return $s1;
  1438. }
  1439.  
  1440. #
  1441. # process_BFI - process any of the B<>, F<>, or I<> pod-escapes and
  1442. # convert them to corresponding HTML directives.
  1443. #
  1444. sub process_BFI {
  1445.     my($tag, $str) = @_;
  1446.     my($s1);            # work string
  1447.     my(%repltext) = (    'B' => 'STRONG',
  1448.             'F' => 'EM',
  1449.             'I' => 'EM');
  1450.  
  1451.     # extract the modified text and convert to HTML
  1452.     $s1 = "<$repltext{$tag}>$str</$repltext{$tag}>";
  1453.     return $s1;
  1454. }
  1455.  
  1456. #
  1457. # process_C - process the C<> pod-escape.
  1458. #
  1459. sub process_C {
  1460.     my($str, $doref) = @_;
  1461.     my($s1, $s2);
  1462.  
  1463.     $s1 = $str;
  1464.     $s1 =~ s/\([^()]*\)//g;    # delete parentheses
  1465.     $s2 = $s1;
  1466.     $s1 =~ s/\W//g;        # delete bogus characters
  1467.     $str = html_escape($str);
  1468.  
  1469.     # if there was a pod file that we found earlier with an appropriate
  1470.     # =item directive, then create a link to that page.
  1471.     if ($doref && defined $items{$s1}) {
  1472.     $s1 = ($items{$s1} ?
  1473.            "<A HREF=\"$htmlroot/$items{$s1}#item_" . htmlify(0,$s2) .  "\">$str</A>" :
  1474.            "<A HREF=\"#item_" . htmlify(0,$s2) .  "\">$str</A>");
  1475.     $s1 =~ s,(perl\w+/(\S+)\.html)#item_\2\b,$1,; 
  1476.     confess "s1 has space: $s1" if $s1 =~ /HREF="[^"]*\s[^"]*"/;
  1477.     } else {
  1478.     $s1 = "<CODE>$str</CODE>";
  1479.     # warn "$0: $podfile: cannot resolve C<$str> in paragraph $paragraph\n" if $verbose
  1480.     }
  1481.  
  1482.  
  1483.     return $s1;
  1484. }
  1485.  
  1486. #
  1487. # process_E - process the E<> pod directive which seems to escape a character.
  1488. #
  1489. sub process_E {
  1490.     my($str) = @_;
  1491.  
  1492.     for ($str) {
  1493.     s,([^/].*),\&$1\;,g;
  1494.     }
  1495.  
  1496.     return $str;
  1497. }
  1498.  
  1499. #
  1500. # process_Z - process the Z<> pod directive which really just amounts to
  1501. # ignoring it.  this allows someone to start a paragraph with an =
  1502. #
  1503. sub process_Z {
  1504.     my($str) = @_;
  1505.  
  1506.     # there is no equivalent in HTML for this so just ignore it.
  1507.     $str = "";
  1508.     return $str;
  1509. }
  1510.  
  1511. #
  1512. # process_S - process the S<> pod directive which means to convert all
  1513. # spaces in the string to non-breaking spaces (in HTML-eze).
  1514. #
  1515. sub process_S {
  1516.     my($str) = @_;
  1517.  
  1518.     # convert all spaces in the text to non-breaking spaces in HTML.
  1519.     $str =~ s/ / /g;
  1520.     return $str;
  1521. }
  1522.  
  1523. #
  1524. # process_X - this is supposed to make an index entry.  we'll just 
  1525. # ignore it.
  1526. #
  1527. sub process_X {
  1528.     return '';
  1529. }
  1530.  
  1531.  
  1532. #
  1533. # finish_list - finish off any pending HTML lists.  this should be called
  1534. # after the entire pod file has been read and converted.
  1535. #
  1536. sub finish_list {
  1537.     while ($listlevel > 0) {
  1538.     print HTML "</DL>\n";
  1539.     $listlevel--;
  1540.     }
  1541. }
  1542.  
  1543. #
  1544. # htmlify - converts a pod section specification to a suitable section
  1545. # specification for HTML.  if first arg is 1, only takes 1st word.
  1546. #
  1547. sub htmlify {
  1548.     my($compact, $heading) = @_;
  1549.  
  1550.     if ($compact) {
  1551.       $heading =~ /^(\w+)/;
  1552.       $heading = $1;
  1553.     } 
  1554.  
  1555.   # $heading = lc($heading);
  1556.   $heading =~ s/[^\w\s]/_/g;
  1557.   $heading =~ s/(\s+)/ /g;
  1558.   $heading =~ s/^\s*(.*?)\s*$/$1/s;
  1559.   $heading =~ s/ /_/g;
  1560.   $heading =~ s/\A(.{32}).*\Z/$1/s;
  1561.   $heading =~ s/\s+\Z//;
  1562.   $heading =~ s/_{2,}/_/g;
  1563.  
  1564.   return $heading;
  1565. }
  1566.  
  1567. BEGIN {
  1568. }
  1569.  
  1570. 1;
  1571.